/*
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "rs_physical_screen_processor.h"

#include "rs_trace.h"
#include "string_utils.h"

#include <fstream>

#include "draw/color.h"
#include "image/bitmap.h"
#include "image/image_info.h"
#include "platform/common/rs_log.h"
#include "utils/data.h"

namespace OHOS {
namespace Rosen {
RSPhysicalScreenProcessor::RSPhysicalScreenProcessor()
    : composerAdapter_(std::make_unique<RSComposerAdapter>())
{
}

RSPhysicalScreenProcessor::~RSPhysicalScreenProcessor() noexcept
{
}

bool RSPhysicalScreenProcessor::Init(RSScreenRenderNode& node, int32_t offsetX, int32_t offsetY, ScreenId mirroredId,
                                     std::shared_ptr<RSBaseRenderEngine> renderEngine)
{
#ifdef RS_ENABLE_GPU
    // planning: adapt isRenderThread
    if (!RSProcessor::Init(node, offsetX, offsetY, mirroredId, renderEngine)) {
        return false;
    }
#endif

    return composerAdapter_->Init(node, screenInfo_, mirroredScreenInfo_, mirrorAdaptiveCoefficient_,
        [this](const auto& surface, const auto& layers) { Redraw(surface, layers); });
}

void RSPhysicalScreenProcessor::PostProcess()
{
    composerAdapter_->CommitLayers(layers_);
}

void RSPhysicalScreenProcessor::ProcessSurface(RSSurfaceRenderNode &node)
{
    if (renderEngine_) {
        composerAdapter_->SetColorFilterMode(renderEngine_->GetColorFilterMode());
    }
    auto layer = composerAdapter_->CreateLayer(node);
    if (layer == nullptr) {
        RS_LOGD("RSPhysicalScreenProcessor::ProcessSurface: failed to createLayer for"
            " node(id: %{public}" PRIu64 ")", node.GetId());
        return;
    }

    layers_.emplace_back(layer);
}

void RSPhysicalScreenProcessor::ProcessScreenSurface(RSScreenRenderNode& node)
{
    RS_LOGI("RSPhysicalScreenProcessor::ProcessScreenSurface() is not supported.");
}

void RSPhysicalScreenProcessor::ProcessRcdSurface(RSRcdSurfaceRenderNode& node)
{
    RS_LOGI("RSPhysicalScreenProcessor::ProcessRcdSurface() is not supported");
}

void RSPhysicalScreenProcessor::Redraw(const sptr<Surface>& surface, const std::vector<RSLayerPtr>& layers)
{
    RS_TRACE_NAME("Redraw");
    if (surface == nullptr || renderEngine_ == nullptr) {
        RS_LOGE("RSPhysicalScreenProcessor::Redraw: surface or renderEngine_ is null");
        return;
    }

    RS_LOGD("RsDebug RSPhysicalScreenProcessor::Redraw flush frame buffer start");
    bool forceCPU = RSBaseRenderEngine::NeedForceCPU(layers);
    RS_LOGI("RsDebug RSPhysicalScreenProcessor::Redraw layers size: %{public}zu, forceCPU: %{public}d", layers.size(), forceCPU);
    RS_LOGI("RsDebug RSPhysicalScreenProcessor::Redraw renderFrameConfig: width %{public}d, height %{public}d, format %{public}d",
        renderFrameConfig_.width, renderFrameConfig_.height, renderFrameConfig_.format);
    auto renderFrame = renderEngine_->RequestFrame(surface, renderFrameConfig_, forceCPU);
    if (renderFrame == nullptr) {
        RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw: failed to request frame.");
        return;
    }

    auto canvas = renderFrame->GetCanvas();
    if (canvas == nullptr) {
        RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw: canvas is nullptr.");
        return;
    }

    if (mirroredScreenInfo_.id != INVALID_SCREEN_ID) {
        canvas->ConcatMatrix(mirrorAdaptiveMatrix_);
        RS_LOGI("RsDebug RSPhysicalScreenProcessor::Redraw mirrorAdaptiveMatrix: "
            "%{public}f, %{public}f, %{public}f, %{public}f, %{public}f, %{public}f, %{public}f, %{public}f, %{public}f",
            mirrorAdaptiveMatrix_.Get(0), mirrorAdaptiveMatrix_.Get(1), mirrorAdaptiveMatrix_.Get(2),
            mirrorAdaptiveMatrix_.Get(3), mirrorAdaptiveMatrix_.Get(4), mirrorAdaptiveMatrix_.Get(5),
            mirrorAdaptiveMatrix_.Get(6), mirrorAdaptiveMatrix_.Get(7), mirrorAdaptiveMatrix_.Get(8));
    } else {
        canvas->ConcatMatrix(screenTransformMatrix_);
        RS_LOGI("RsDebug RSPhysicalScreenProcessor::Redraw screenTransformMatrix: "
            "%{public}f, %{public}f, %{public}f, %{public}f, %{public}f, %{public}f, %{public}f, %{public}f, %{public}f",
            screenTransformMatrix_.Get(0), screenTransformMatrix_.Get(1), screenTransformMatrix_.Get(2),
            screenTransformMatrix_.Get(3), screenTransformMatrix_.Get(4), screenTransformMatrix_.Get(5),
            screenTransformMatrix_.Get(6), screenTransformMatrix_.Get(7), screenTransformMatrix_.Get(8));
    }

    renderEngine_->DrawLayers(*canvas, layers, forceCPU);
    {
        const auto& surfaceFrame = renderFrame->GetFrame();
        if (surfaceFrame) {
            auto drawingSurface = surfaceFrame->GetSurface();
            if (drawingSurface) {
                auto image = drawingSurface->GetImageSnapshot();
                if (image) {
                    int32_t w = image->GetWidth();
                    int32_t h = image->GetHeight();
                    if (w > 0 && h > 0) {
                        Drawing::Bitmap bitmap;
                        Drawing::BitmapFormat format = { Drawing::COLORTYPE_RGBA_8888, Drawing::ALPHATYPE_PREMUL };
                        bitmap.Build(1, 1, format);
                        if (image->ReadPixels(bitmap, w / 2, h / 2)) {
                            auto color = bitmap.GetColor(0, 0);
                            RS_LOGI("RsDebug RSPhysicalScreenProcessor::Redraw center pixel color: r=%{public}u, g=%{public}u, b=%{public}u, a=%{public}u",
                                Drawing::Color::ColorQuadGetR(color), Drawing::Color::ColorQuadGetG(color),
                                Drawing::Color::ColorQuadGetB(color), Drawing::Color::ColorQuadGetA(color));
                        } else {
                            RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw ReadPixels failed");
                        }
                    } else {
                        RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw invalid image size: %{public}d, %{public}d", w, h);
                    }

                    auto data = image->EncodeToData(Drawing::EncodedImageFormat::PNG, 100);
                    if (data && data->GetSize() > 0) {
                        std::string path = "/data/local/tmp/rs_debug_screenshot_" + std::to_string(renderFrameConfig_.width) + "x" + std::to_string(renderFrameConfig_.height) + ".png";
                        std::ofstream outFile(path, std::ios::binary);
                        if (outFile.is_open()) {
                            outFile.write(reinterpret_cast<const char*>(data->GetData()), data->GetSize());
                            outFile.close();
                            RS_LOGI("RsDebug RSPhysicalScreenProcessor::Redraw dumped image to %{public}s", path.c_str());
                        } else {
                            RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw failed to open file for dumping image: %{public}s", path.c_str());
                        }
                    } else {
                        RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw EncodeToData failed or empty data");
                    }
                } else {
                    RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw GetImageSnapshot failed");
                }
            } else {
                RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw GetSurface failed");
            }
        } else {
            RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw GetFrame failed");
        }
    }
    renderFrame->Flush();
    RS_LOGD("RsDebug RSPhysicalScreenProcessor::Redraw flush frame buffer end");
}
} // namespace Rosen
} // namespace OHOS
